#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of_irq.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>

#define SEG_WHICH _IOW('k',0,int)
#define SEG_DAT _IOW('k',1,int)

unsigned char code[] = {
    0x3f, // 0
    0x06, // 1
    0x5b, // 2
    0x4f, // 3
    0x66, // 4
    0x6d, // 5
    0x7d, // 6
    0x07, // 7
    0x7f, // 8
    0x6f, // 9
    0x77, // A
    0x7c, // b
    0x39, // c
    0x5e, // d
    0x79, // e
    0x71, // f
};

unsigned char which[] = {
    0x1, // sg0
    0x2, // sg1
    0x4, // sg2
    0x8, // sg3
};

struct work_struct work; // 分配工作队列对象

#define CNAME "m74hc595"
struct i2c_client* cli = NULL;

struct spi_device* cspi = NULL;

// 分布实现字符设备相关
struct cdev* cdev = NULL;
dev_t devn = -1;

// 自动创建设备节点相关
struct class* cls = NULL;
struct device* dev = NULL;

int dat = 0;
const struct of_device_id match_table[] = {
    { .compatible = "hqyj,m74hc595" },
    {}
};

// 中断底半部处理函数
void work_func(struct work_struct* work1)
{
    int a, b, c, d;
    a = dat / 1000;
    b = dat / 100 % 10;
    c = dat / 10 % 10;
    d = dat % 10;
    spi_write(cspi, which + 0, 1);
    spi_write(cspi, code + a, 1);
    mdelay(1);
    spi_write(cspi, which + 1, 1);
    spi_write(cspi, code + b, 1);
    mdelay(1);
    spi_write(cspi, which + 2, 1);
    spi_write(cspi, code + c, 1);
    mdelay(1);
    spi_write(cspi, which + 3, 1);
    spi_write(cspi, code + d, 1);
    mdelay(1);
    // printk("a=%d b=%d c=%d d=%d\n",a,b,c,d);
    // mdelay(1000);
    schedule_work(&work); // 调用中断底半部执行
}

long m74hc595_ioctl(struct file* file, unsigned int cmd, unsigned long args)
{
    dat = (int)args;

    schedule_work(&work); // 调用中断底半部执行
    return 0;
}

int m74hc595_close(struct inode* inode, struct file* file)
{
    char buf[]={0xf,0x00};
    printk("__%d__:%s:%s\n", __LINE__, __func__, __FILE__);
    
    cancel_work_sync(&work); // 取消工作队列
    spi_write(cspi,buf,2);
    return 0;

}
const struct file_operations fops = {
    .unlocked_ioctl = m74hc595_ioctl,
    .release = m74hc595_close,
};

int probe(struct spi_device* spi)
{
    printk("__%d__:%s:%s\n", __LINE__, __func__, __FILE__);
    cspi = spi;
    INIT_WORK(&work, work_func); // 对象初始化

    /**********分布实现字符设备**************/
    cdev = cdev_alloc();
    if (!cdev) {
        printk("__%d__:cdev_alloc is error\n", __LINE__);
        goto ERR1;
    }
    cdev_init(cdev, &fops); // 初始化对象
    // 动态分配对象
    if (alloc_chrdev_region(&devn, 0, 1, CNAME)) {
        printk("__%d__:alloc_chrdev_region is error\n", __LINE__);
        goto ERR2;
    }
    // 注册对象
    if (cdev_add(cdev, devn, 1)) {
        printk("__%d__:cdev_add is error\n", __LINE__);
        goto ERR3;
    }
    /**********自动创建设备节点**************/
    cls = class_create(THIS_MODULE, CNAME);
    if (IS_ERR(cls)) {
        printk("__%d__:class_create is error\n", __LINE__);
        goto ERR4;
    }
    dev = device_create(cls, NULL, devn, NULL, CNAME);
    if (IS_ERR(dev)) {
        printk("__%d__:device_create is error\n", __LINE__);
        goto ERR5;
    }

    return 0;

ERR5:
    class_destroy(cls);
ERR4:
    cdev_del(cdev);
ERR3:
    unregister_chrdev_region(devn, 1);
ERR2:
    kfree(cdev);
ERR1:
    return -1;
}
int remove(struct spi_device* spi)
{
    printk("__%d__:%s:%s\n", __LINE__, __func__, __FILE__);
    cancel_work_sync(&work); // 取消工作队列

    device_destroy(cls, devn);
    class_destroy(cls);

    cdev_del(cdev); // 注销对象
    unregister_chrdev_region(devn, 1);
    kfree(cdev);
    return 0;
}

struct spi_driver m74hc595 = {
    .probe = probe,
    .remove = remove,
    .driver = {
        .name = "xxxx",
        .of_match_table = match_table,
    },
};
module_spi_driver(m74hc595);
MODULE_LICENSE("GPL");